#include "device_search.h"
#include "ui_device_search.h"

Device_search::Device_search(QWidget *parent) :QDialog(parent),ui(new Ui::Device_search)
{
    ui->setupUi(this);

    // 初始化UDP和TCP套接字
    udpsocket = new QUdpSocket(this);
    tcpsocket = new QTcpSocket(this);

    // 连接搜索按钮的点击信号到seekSlots槽函数
    connect(ui->seekButton, SIGNAL(clicked()), this, SLOT(seekSlots()));
    // 连接连接按钮的点击信号到connectSlots槽函数
    connect(ui->connectButton, SIGNAL(clicked()), this, SLOT(connectSlots()));

    // 连接UDP套接字的readyRead信号到readUDPslots槽函数 数据到达时触发
    connect(udpsocket, SIGNAL(readyRead()), this, SLOT(readUDPslots()));

    // 连接TCP套接字的readyRead信号到readSlots槽函数
    connect(tcpsocket, SIGNAL(readyRead()), this, SLOT(readSlots()));

    // 升级按钮
    connect(ui->upgradeButton, SIGNAL(clicked()), this, SLOT(upgradeSlots()));

    // 选择文件按钮
    connect(ui->pushButtonOpen, SIGNAL(clicked()), this, SLOT(openSlots()));

    // 初始化显示
    ui->textBrowser->append("设备 IP-Port:");

    // 先禁止升级和选择文件按钮
    ui->upgradeButton->setEnabled(false);
    ui->pushButtonOpen->setEnabled(false);
}

Device_search::~Device_search()
{
    delete ui;
}

// 搜索设备
void Device_search::seekSlots()
{
    // 获取用户输入的IP和端口
    QString ip = ui->lineEdit->text();
    int port = ui->spinBox->value();

    // 准备要发送的UDP数据包 MaiSui
    QByteArray datagram = "MaiSui";

    // 通过UDP套接字向指定的IP地址和端口发送数据包
    udpsocket->writeDatagram(datagram.data(), datagram.size(),
                             QHostAddress(ip), port);
}

// UDP数据接收处理
void Device_search::readUDPslots()
{
    // 循环处理所有待处理的UDP数据包
    while (udpsocket->hasPendingDatagrams())
    {
        // 读取UDP数据包，将数据存储在buf中，并获取发送方的IP地址和端口号
        udpsocket->readDatagram(buf, sizeof(buf), &senderIp, &senderPort);

        // 检查接收到的数据包内容是否为"yes"
        if (strcmp(buf, "yes") != 0)
        {
            return;
        }

        // 过滤无效IP地址,如本地回环地址或空地址
        if (senderIp.isLoopback() || senderIp.isNull())
            continue;

        QString senderip = senderIp.toString();
        // 格式化显示文本，将IP地址和端口号拼接在一起
        QString displayText = QString("%1--%2").arg(senderip).arg(senderPort);

        // 检查ComboBox中是否已存在该IP地址和端口号的设备
        bool exists = false;
        for (int i = 0; i < ui->comboBox->count(); ++i)
        {
            // 获取ComboBox中第i项的数据
            QVariant data = ui->comboBox->itemData(i);
            // 将数据转换为QVariantMap类型
            QVariantMap ipPortData = data.toMap();

            // 检查IP地址和端口号是否与当前接收到的设备相同
            if (ipPortData["ip"] == senderip && ipPortData["port"] == senderPort)
            {
                exists = true;
                break;
            }
        }

        // 如果ComboBox中不存在该设备，则将其添加到ComboBox中
        if (!exists)
        {
            // QVariantMap实际上是QMap<QString, QVariant>的别名，键值对容器
            QVariantMap ipPortData;
            ipPortData["ip"] = senderip;
            ipPortData["port"] = senderPort;
            ui->comboBox->addItem(displayText, QVariant(ipPortData));
            qDebug() << "Added device:" << displayText;
            ui->textBrowser->append(displayText);
        }
    }
}


// 连接选定设备
void Device_search::connectSlots()
{
    // 获取用户在ComboBox中当前选中的设备索引
    int index = ui->comboBox->currentIndex();
    if (index == -1)
    {
        QMessageBox::critical(this, "错误", "未选择设备！");
        return;
    }

    // 提取被连接设备的IP和端口信息
    // 获取ComboBox中当前选中项的数据
    QVariant data = ui->comboBox->itemData(index);
    // 将数据转换为QVariantMap类型
    QVariantMap ipPortData = data.toMap();
    ipstring = ipPortData["ip"].toString();
    portstring = ipPortData["port"].toUInt();

    qDebug() << "ip:" << ipstring;
    qDebug() << "port:" << portstring;

    // 断开现有连接（如果存在）state用于返回套接字的状态
    if (tcpsocket && tcpsocket->state() != QAbstractSocket::UnconnectedState)
    {
        tcpsocket->disconnectFromHost();
        if (tcpsocket->state() != QAbstractSocket::UnconnectedState)
        {
            tcpsocket->waitForDisconnected(1000); // 等待 1 秒断开
        }
    }

    // 确保tcpsocket已初始化
    if (!tcpsocket)
    {
        tcpsocket = new QTcpSocket(this); // 确保 tcpsocket 已初始化
    }

    // 建立新连接到设备 TCP
    tcpsocket->connectToHost(ipstring, portstring);

    // 等待连接成功
    if (tcpsocket->waitForConnected(3000))
    {
        QMessageBox::information(this, "连接状态", "设备已连接");

        ui->pushButtonOpen->setEnabled(true);
        ui->upgradeButton->setEnabled(true);
    }
}

// TCP接收槽函数
void Device_search::readSlots()
{
    // 读取所有TCP数据
    QByteArray data = tcpsocket->readAll();
    qDebug() << "Received data:" << data;

    // 解析JSON数据
    QJsonDocument jsonDoc = QJsonDocument::fromJson(data); // 从字节数组解析JSON
    QJsonObject rootObj = jsonDoc.object();  // 获取根对象

    int type = rootObj["type"].toInt(); // 获取消息类型

    switch (type)
    {
    case 1: // 版本检查响应
    {
        QJsonObject dataObj = rootObj["data"].toObject();  // 获取数据对象
        bool needUpdate = dataObj["update"].toBool();
        qDebug() << "Version check response - needUpdate:" << needUpdate;

        if (needUpdate)
        {
            ui->textBrowser->append("数据类型一--服务器回复：需要升级");
            ui->upgradeButton->setEnabled(true);
        }
        else
        {
            ui->textBrowser->append("数据类型二--服务器回复：已是最新版本，无需升级");
        }
        break;
    }
    case 2: // 文件传输结果
    {
        QJsonObject dataObj = rootObj["data"].toObject();
        QString transResult = dataObj["trans_success"].toString();
        bool transSuccess = (transResult == "true"); // 字符串比较

        qDebug() << "File transfer result:" << transResult << "->" << transSuccess;

        if (transSuccess)
        {
            ui->textBrowser->append("数据类型二--文件传输成功");
        }
        else
        {
            ui->textBrowser->append("数据类型二--文件传输失败");
            QMessageBox::warning(this, "传输失败", "文件传输过程中出现错误");
        }
        break;
    }
    case 3: // 升级结果
    {
        QJsonObject dataObj = rootObj["data"].toObject();
        QString upgradeResult = dataObj["update_success"].toString();
        bool upgradeSuccess = (upgradeResult == "true"); // 字符串比较

        qDebug() << "Upgrade result:" << upgradeResult << "->" << upgradeSuccess;

        if (upgradeSuccess)
        {
            ui->textBrowser->append("数据类型三--升级成功完成");
            QMessageBox::information(this, "成功", "升级流程已完成");
        }
        else
        {
            ui->textBrowser->append("数据类型三--升级过程失败");
            QMessageBox::critical(this, "失败", "升级过程中出现错误");
        }
        break;
    }
    default:
        qDebug() << "Unknown message type received:" << type;
        ui->textBrowser->append("收到未知类型的消息");
        break;
    }
}


// 计算文件MD5值
QString Device_search::calculateFileMD5(const QString &filePath)
{
    QFile file(filePath);

    if (!file.open(QIODevice::ReadOnly)) // 以只读模式打开文件
    {
        qDebug() << "无法打开文件计算MD5:" << filePath;
        return"";
    }

    // 创建MD5哈希对象
    QCryptographicHash hash(QCryptographicHash::Md5);
    if (hash.addData(&file))
    {
        file.close();
        // 返回十六进制字符串形式的MD5值
        return hash.result().toHex();
    }
    else
    {
        qDebug() << "计算MD5失败:" << filePath;
        return "";
    }
}


// 选择升级文件
void Device_search::openSlots()
{

    ui->textBrowserOpen->append("升级文件...");

    // 过滤器
    QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.ui *.pro)";
    // 打开文件选择对话框
    readPath = QFileDialog::getOpenFileName(this, "打开", "C:/Users/90613/Desktop", filter);
    ui->textBrowserOpen->append(readPath);


    // 创建文件信息类对象
    QFileInfo info(readPath);

    if (!info.isFile()) // 如果不是文件
    {
        QMessageBox::warning(this, "提示", readPath.append("请检查要读取的文件路径！"));
        return;
    }
    if (!info.isReadable()) // 如果不可读
    {
        QMessageBox::warning(this, "提示", readPath.append("请检查文件权限！"));
        return;
    }


    // 获取上次更新时间
    QString time = info.lastModified().toString("yyyy-MM-dd hh:mm:ss");
    // 显示
    ui->textBrowserOpen->append(time.prepend("修改时间："));
    // 获取文件大小
    qint64 size = info.size();
    QString text = QString::number(size).prepend("文件大小：").append("字节");
    ui->textBrowserOpen->append(text);

    // 发送升级询问包
    // 构造并发送JSON数据

    QJsonObject request;

    QJsonObject data;
    data["ver"] = "v1.0.1"; // 应从文件中获取实际版本
    data["file_name"] = info.fileName(); // 文件名称
    data["file_len"] = info.size(); // 文件大小

    // 计算MD5值
    QString md5 = calculateFileMD5(readPath);
    if (!md5.isEmpty())
    {

        data["md5"] = md5;// 文件MD5

        qDebug() << "文件MD5:" << md5;
    }
    else
    {
        QMessageBox::warning(this, "MD5错误", "无法计算文件MD5值");
        return;
    }

    request["type"] = 1;
    request["data"] = data;

    // 序列化为JSON文档
    QJsonDocument doc(request);

    QByteArray jsonData = doc.toJson(QJsonDocument::Indented);

    // 发送数据
    qint64 bytesWritten = tcpsocket->write(jsonData);

    if (bytesWritten == -1)
    {
        qDebug() << "发送失败:" << tcpsocket->errorString();
        ui->textBrowser->append("发送失败: " + tcpsocket->errorString());
    }
    else
    {
        qDebug() << "已发送升级问询包，大小:" << bytesWritten << "字节";
        ui->textBrowser->append("已发送升级问询包，大小: " + QString::number(bytesWritten) + " 字节");
    }
}

// 文件升级子线程
void Device_search::upgradeSlots()
{

    if (readPath.isEmpty())
    {
        QMessageBox::warning(this, "提示", "请选择升级文件");
        return;
    }

    qDebug() << "readPath:" << readPath;

    // 创建子线程对象
    mt = new MyThread(ipstring, portstring, readPath, this);

    connect(mt, SIGNAL(progressUpdated(int)), this, SLOT(on_progressUpdated(int)));
    connect(mt, SIGNAL(transferFinished(bool)), this, SLOT(on_transferFinished(bool)));

    // 启动子线程
    mt->start();
}

// 进度更新槽函数实现
void Device_search::on_progressUpdated(int percent)
{
    ui->progressBar->setValue(percent);
    ui->textBrowser->append(QString("升级进度: %1%").arg(percent));
}

// 传输完成槽函数实现
void Device_search::on_transferFinished(bool success)
{

    if (success)
    {
        QMessageBox::information(this, "成功", "文件传输完成");
        // 等待服务器返回升级结果（需添加TCP接收处理）

        ui->progressBar->setValue(0); // 清零进度条
    }
    else
    {
        QMessageBox::critical(this, "失败", "文件传输失败");

        ui->progressBar->setValue(0); // 清零进度条
    }
}
